// Created: Dec 13, 2014
//
// Author: wjinwen.1988@gmail.com 

#ifndef CHI_NET_EVENT_LOOP_H
#define CHI_NET_EVENT_LOOP_H

#include <stdint.h>
#include <sys/time.h>
#include <pthread.h>

#include <vector>
#include <map>
#include <atomic>

#include <event.h>

#include "chi/net/callbacks.h"
#include "chi/mutex.h"

namespace chi {
namespace net {

class Channel;
class EventLoop;

// 定时器回调参数
struct Timer {
  uint64_t timerid;
  EventLoop* loop;
  event* timer_ev;
  struct timeval tv;
  TimeoutCallback cb;
};

// EventLoop必须在同一个线程中定义、初始化，保证事件处理的一致性。
class EventLoop {
 public:
  EventLoop();
  ~EventLoop();

  EventLoop(const EventLoop&) = delete;
  void operator= (const EventLoop&) = delete;

  // Loops forever.
  // Must be called in the same thread as creation of the object.
  void Loop();
  void Quit();

  // Runs callback immediately in the loop thread.
  // It wake up the loop, and run the cb.
  // If in the same loop thread, cb is run within the function.
  // Safe to call from other threads.
  void RunInLoop(Functor&& cb);
  // Queues callback in the loop thread.
  // Runs after finish pooling.
  // Safe to call from other threads.
  void QueueInLoop(Functor&& cb);

  // Run callback after @c delay seconds.
  // Safe to call from other threads.
  void RunAfter(double delay, TimeoutCallback&& cb);
  // Run callback every @c interval seconds.
  // Safe to call from other threads.
  void RunEvery(double interval, TimeoutCallback&& cb);

  void Cancel(uint64_t timerid);

  // Internal usage
  void Wakeup();

  void UpdateChannel(Channel* ch);

  void HandleEvent(int fd, short what);

  // Verify that current thread is EventLoop thread, if EventLoop is running.
  bool IsInEvenLoop() {
    auto tid = loop_thread_.load(std::memory_order_relaxed);
    return tid == 0 || pthread_equal(tid, pthread_self());
  }

 private:
  // Add the timer according to the input parameters.
  // Not thread safe, but in loop.
  void AddTimer(Timer* timer);
  // Delete the specified timer.
  // Not thread safe, but in loop.
  void DelTimer(uint64_t timerid);

  void HandleRead();
  // Call all of the pending functions.
  // Not thread safe, but in loop.
  void DoPendingFunctors();

  typedef std::pair<Channel*, event*> ChannelPair;
  typedef std::map<int, ChannelPair> ChannelMap;
  typedef std::map<int64_t, Timer*> TimerMap;

  bool looping_;
  bool quit_;
  bool calling_pending_functors_;
  // The ID of the thread running the main loop.
  // 0 if loop is not running.
  // Note: POSIX doesn't guarantee that 0 is an invalid pthread_t (or
  // even that atomic<pthread_t> is valid), but that's how it is
  // everywhere (at least on Linux, FreeBSD, and OSX).
  std::atomic<pthread_t> loop_thread_;
  event_base* base_;
  int wakeup_fd_;
  Channel* wakeup_channel_;
  ChannelMap channels_;
  uint64_t timer_sequence_;
  TimerMap timers_;
  MutexLock mutex_;
  std::vector<Functor> pending_functors_;
};

}
}

#endif // NJITSNET_EVENT_LOOP_H

